# -*- coding: UTF-8 -*-
# class Client/Server Code
# transmission protocol: IPV4, TCP
# Client func(): connectWifi, initSocket, TCP_Transmission, timeOutCheck
# Server func(): initAP, initSocket, TCP_acceptModel, TCP_Server
# usage: instance, revmsg = instance.run(sndmsg, revmsg) in loops.

from machine import Timer
import usocket as socket
import network
import utime

class client(object):
    def __init__(self, sta, ssid, password, port):
        self.sta = sta
        self.SSID = ssid
        self.PASSWORD = password
        self.PORT = port

        self.sndmsg = ''
        self.revmsg = ''
        self.t_init = 0
        self.wlanStatus = 0
        self.wifiTimeOutCnt = 0
        self.connectTimeOutCnt = 0
        self.connectStatus = 0
        self.networkStatus = 1
        self.blocking = 0
        self.tcp_addr = '192.168.1.1'
        self.tur = (self.tcp_addr, self.PORT)

    def initSocket(self):
        if not self.connectTimeOutCnt:
            self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            print('client initialized')
            self.client.setblocking(False)
        try:
            # timeOutCheck running
            self.client.connect(self.tur)
            self.connectTimeOutCnt = 0
            print('connection success')
            return 1
        except:
            self.networkStatus = 0
            if self.blocking:
                print("connection failed")
                print("trying to reconnect...")
                return 0
            pass
    
    def logOffSocket(self):
        del self.client

    def initTimeOutCheck(self):
        self.check = Timer(0)
        self.check.init(type = Timer.PERIODIC, callback = self.timeOutCheck, ticks = 300)

    def timeOutCheck(self, args):
        if self.networkStatus == 0:
            print('timeOutCheck is running')
            self.blocking = 1
        else:
            self.blocking = 0

    def connectWifi(self):
        if not self.wlanStatus:
            self.sta.active(True)
        try:
            while True:
                list = self.sta.scan()
                for tur in list:
                    if tur[0] == self.SSID:
                        self.sta.connect(self.SSID, self.PASSWORD, security = network.AUTH_PSK)
                        self.wifiTimeOutCnt = 0
                        self.wlanStatus = 1
                        break
                if self.wlanStatus:
                    break
        except:
            self.networkStatus = 0
            if self.blocking:
                print("Cannot connect correspond wlan...")
                if self.wifiTimeOutCnt <= 3:
                    print("try to reconnect, trying {}".format(self.wifiTimeOutCnt))
                    utime.sleep(5)
                    self.wifiTimeOutCnt += 1
                else:
                    print("reconnnect failed!")
                    self.sta.active(False)
            pass

    def TCP_Transmission(self):
        revmsg = self.revmsg
        sndmsg = bytes(str(self.sndmsg), 'utf-8')
        if sndmsg == '' or sndmsg == '\n':
            print("can't transform nothing!")
        elif sndmsg != 'over':
            try:
                self.client.send(sndmsg)
                print("already send msg to server")
                # receive data from server
                print("receiving..")
                data = self.client.recv(1024)
                revmsg = str(data, 'utf-8')
                self.revmsg = revmsg
                # handle data, something like function()
                if data:
                    print('Client recv: ', str(data, 'utf-8'))
                else:
                    print('now there\'s no data from server')
                return 1
            except:
                pass

    def run(self, sndmsg, revmsg):
        self.sndmsg = sndmsg
        self.revmsg = revmsg

        if not self.t_init:
            self.t_init = 1

            self.initTimeOutCheck()

        if not self.wlanStatus:
            self.connectWifi()
            self.wlanStatus = 1

        if not self.connectStatus:
            self.connectTimeOutCnt = 0
            if not self.initSocket():
                self.connectTimeOutCnt = 1
            else:
                self.connectStatus = 1
                self.connectTimeOutCnt = 0

        if self.connectStatus:
            mark = self.TCP_Transmission()
            if not mark:
                self.connectStatus = 0
                self.logOffSocket()
                self.check.deinit()
                self.t_init = 0
                # self.wlanStatus = 0

        sndmsg = self.sndmsg
        return self.revmsg

# -------------------------------------------------------------------------
# server class
# -------------------------------------------------------------------------

class server(object):
    def __init__(self, ap, ssid, password, port):
        self.ap = ap
        self.SSID = ssid
        self.PASSWORD = password
        self.PORT = port

        self.t_init = 0
        self.apStatus = 0
        self.acceptStatus = 0
        self.remoteNetworkStatus = 1
        self.connectTimeOutCnt = 0
        self.localBlocking = 0
        self.remoteBlocking = 0
        self.tur = ('0.0.0.0', self.PORT)
        self.sndmsg = 0
        self.revmsg = 0

    def initAP(self):
        self.ap.active(False)
        self.ap.config(self.SSID, self.PASSWORD, security = network.AUTH_PSK)
        self.ap.active(True)
    
    def initSocket(self):
        self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.serv.setblocking(False)
        self.serv.bind(self.tur)
        self.serv.listen(2)

    def logOffLoaclSocket(self):
        del self.serv

    def logOffRemoteSocket(self):
        del self.conn

    def initTimeOutCheck(self):
        self.remoteSocketCheck = Timer(0)
        self.remoteSocketCheck.init(type = Timer.PERIODIC, callback = self.remoteTimeOutCheck, ticks = 300)

    def remoteTimeOutCheck(self, args):
        if self.remoteNetworkStatus == 0:
            print('timeOutCheck is running')
            print('blocking!')
            self.remoteBlocking = 1
        else:
            self.remoteBlocking = 0

    def TCP_accpetModel(self):
        try:
            # timeOutCheck running
            print("accepting...")
            self.conn, self.clientAddr = self.serv.accept()
            print('Connected By ', self.clientAddr)
            self.conn.setblocking(False)
            return 1
        except:
            self.remoteNetworkStatus = 0
            if self.remoteBlocking:
                print('no connected device for now')
                print("try to reconnect, trying: {}".format(self.connectTimeOutCnt))
                return 0
            pass

    def TCP_Server(self):
        sndmsg = bytes(str(self.sndmsg), 'utf-8')
        revmsg = self.revmsg
        try:
            print("recv data...")
            data = self.conn.recv(1024)
            revmsg = str(data, 'utf-8')
            print("recv success")
            self.remoteNetworkStatus = 1
            self.connectTimeOutCnt = 0
            print('recv message: {}' .format(revmsg))
            self.revmsg = revmsg
            self.conn.send(sndmsg)
            self.revmsg = revmsg
            print("already send msg to client")
            return 1
            # elif revmsg == 'over server':
            #     print('recvive close command from client')
            #     self.conn.close()
            #     return 0
        except:
            self.remoteNetworkStatus = 0
            if self.remoteBlocking:
                print("can not send or recv data from client")
                print("try to reconnect, trying {}".format(self.connectTimeOutCnt))
                utime.sleep_ms(100)
                return 0
            pass

    def run(self, sndmsg, revmsg):
        self.sndmsg = sndmsg
        self.revmsg = revmsg

        if not self.t_init:
            self.t_init = 1

            self.initTimeOutCheck()
        
        if not self.apStatus:
            self.apStatus = 1

            self.initAP()
            self.initSocket()

        if not self.acceptStatus:
            self.connectTimeOutCnt = 0
            while self.connectTimeOutCnt <= 1000:
                if not self.TCP_accpetModel():
                    self.connectTimeOutCnt += 1
                else:
                    self.acceptStatus = 1
                    self.remoteNetworkStatus = 1
                    self.connectTimeOutCnt = 0
                    break
            if not self.acceptStatus:
                print("reconnect failed!")
                self.apStatus = 0
                self.check.deinit()
                self.t_init = 0
                self.logOffLoaclSocket()

        if self.remoteNetworkStatus:
            print("accept!")
            mark = self.TCP_Server()
            if not mark:
                self.logOffRemoteSocket()
                self.logOffLoaclSocket()
                self.acceptStatus = 0
                self.check.deinit()
                self.t_init = 0
        sndmsg = self.sndmsg
        return self.revmsg

# client example:

# from machine import Timer
# import usocket as socket
# import network
# import utime

# SSID = 'TEST_Wafflenano'
# PASSWORD = '1234567890'
# PORT = 5000

# sta = network.WLAN(network.STA_IF)
# client = client(sta, SSID, PASSWORD, PORT)

# cnt = 0
# sndmsg = "hello"
# revmsg = []
# while cnt <= 1000:
#     revmsg = sta.run(sndmsg, revmsg)
#     print(revmsg)
#     utime.sleep_ms(10)
#     cnt += 1
